/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2023. All rights reserved.
 * HS1304, gmac module of 1711, Copyright (c) 2023-, Huawei
 *
 *
 *
 * This code is licensed under the GNU GPLv2 and later.
 */

#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "hw/sysbus.h"
#include "migration/vmstate.h"
#include "net/net.h"
#include "net/eth.h"
#include "hw/hw.h"
#include "hw/irq.h"
#include "hw/ptimer.h"
#include "hw/qdev-properties.h"
#include "qemu/log.h"
#include "qemu/module.h"
#include "exec/address-spaces.h"
#include "net/checksum.h"

#ifdef HS1304_DEBUG
#define DEBUG_PRINT(...) info_report(__VA_ARGS__)
#else
#define DEBUG_PRINT(...)
#endif 

#define TYPE_HS1304_GMAC "hs1304.gmac"

#define HS1304_GMAC(obj) OBJECT_CHECK(hs1304_gmac_state, (obj), TYPE_HS1304_GMAC)

/* MAC */
#define DUPLEX_TYPE_REG                 0x0008
#define MAX_FRM_SIZE_REG                0x003C
#define PORT_MODE_REG                   0x0040
#define PORT_EN_REG                     0x0044
#define PAUSE_EN_REG                    0x0048
#define TRANSMIT_CONTROL_REG            0x0060
#define LINE_LOOP_BACK_REG              0x01A8
#define CF_CRC_STRIP_REG                0x01B0
#define MODE_CHANGE_EN_REG              0x01B4
#define LOOP_REG                        0x01DC
#define RECV_CONTROL_REG                0x01E0
#define STATION_ADDR_LOW_2_REG          0x0210
#define STATION_ADDR_HIGH_2_REG         0x0214
#define MAC_SKIP_LEN                    0x0240

/* PCU */
#define MAX_FRAME_LEN_REG               0x0444
#define TX_CFF_ADDR_0_REG               0x0488
#define TX_CFF_ADDR_1_REG               0x048C
#define TX_CFF_ADDR_2_REG               0x0490
#define TX_CFF_ADDR_3_REG               0x0494
#define TX_FIFO_THRSLD_REG              0x0420
#define TX_CS_FAIL_CNT_REG              0x0460
#define TX_TRANS_PKG_CNT_REG            0x0468
#define TX_PAUSE_REG                    0x0470

#define WE_ADDR_REG                     0x04E0

#define DEBUG_ST_MCH_REG                0x0450
#define FIFO_CURR_STATUS_REG            0x0454
#define FIFO_HIS_STATUS_REG             0x0458
#define FIFO_THRSLD_REG                 0x0428
#define CFF_DATA_NUM_REG                0x045C

#define RX_FIFO_THRSLD_REG              0x0424
#define RX_CFF_ADDR_REG                 0x04A0
#define RX_BUF_REQ_ADDR_REG             0x04EC
#define RX_CTRL_REG                     0x04F0
#define RX_PKT_MODE_REG                 0x04F4
#define RX_TRANS_PKG_CNT_REG            0x0464
#define RX_ADDR_OVERFLOW_REG            0x046C
#define RX_BUF_SIZE_REG                 0x04E4
#define BUS_CTRL_REG                    0x04E8
#define QOS_MODE_REG                    0x04F8
#define IP_QOS0_REG                     0x0500
#define GRP_MODE0_REG                   0x0520
#define GRP_MODE1_REG                   0x0524
#define TT_MODE_REG                     0x0528
#define TAG_MODE_REG                    0x052C

#define INTRPT_MSK_REG                  0x042C
#define INTRPT_STAT_REG                 0x0434
#define INTRPT_CLR_REG                  0x0438

#define MAX_OFFSET  0x5E4
#define MAX_REG_OFFSET  (MAX_OFFSET >> 2)

#define RX_FIFO_DEPTH 256
#define TX_FIFO_DEPTH 64
#define PACKET_HEAD_SIZE 64
#define PACKET_DATA_SIZE 0x600

#define ETH_HALF_DUPLEX 0
#define ETH_FULL_DUPLEX 1

#define MMAC_10M    0
#define MMAC_100M   1

/* 以太网MAC帧的最小,最大长度(包括帧头) */
#define MIN_MAC_FRAME_LEN 60   /* 46(数据域)*/
#define MAX_MAC_FRAME_LEN 1514 /* 1500(数据域)*/

#define REG_SHIFT 2

enum {
    TYPE_ARP = 0,
    TYPE_RARP,
    TYPE_MUL,
    TYPE_BRC,
    TYPE_ICMP,
    TYPE_IGMP,
    TYPE_TCP,
    TYPE_UDP,
    TYPE_IP,
    TYPE_OTH,
    TYPE_VLAN,
    TYPE_MAX,
};

typedef union {
    uint32_t val;
    struct {
        uint32_t l4_cs      : 1;
        uint32_t cl         : 1;
        uint32_t l3_cs      : 1;
        uint32_t len_ro     : 16;
        uint32_t ds         : 6;
        uint32_t sd         : 1;
        uint32_t ipoff      : 5;
        uint32_t res        : 1;
    } bits;
} str_tx_word0;

typedef union {
    uint32_t val;
    struct {
        uint32_t pool       : 4;
        uint32_t send_len   : 16;
        uint32_t back       : 6;
        uint32_t fm         : 1;
        uint32_t cache      : 1;
        uint32_t txif_1588  : 2;
        uint32_t back_ind8  : 1;
        uint32_t res        : 1;
    } bits;
} str_tx_word1;

typedef struct {
    uint32_t first_skip; // get value from regs

    union {
        uint32_t val;
        struct {
            uint32_t res0   : 3;
            uint32_t tt     : 2;
            uint32_t grp    : 4;
            uint32_t Qos    : 3;
            uint32_t Gen_id : 8;
            uint32_t res1   : 12;
        }bits;
    } word0;
    
    union {
        uint32_t tag;
    } word1;

    union {
        uint32_t val;
        struct {
            uint32_t all_skip   : 9; // skip1+skip2
            uint32_t res        : 3;
            uint32_t port_num   : 4;
            uint32_t len        : 16;
        }bits;
    } word2;

    union {
        uint32_t val;
        struct {
            uint32_t vlan       : 16;
            uint32_t lp_offset  : 8;
            uint32_t buf_num    : 8;
        }bits;
    } word3;

    union {
        uint32_t val;
        struct {
            uint32_t res            : 2;
            uint32_t ptp_err        : 1;
            uint32_t ptp_flag       : 1;
            uint32_t res1           : 1;
            uint32_t pm             : 2;
            uint32_t key_match      : 1;
            uint32_t l2_err         : 1;
            uint32_t l3_err         : 4;
            uint32_t drop           : 1;
            uint32_t vlan_flag      : 1;
            uint32_t icmp           : 1;
            uint32_t rarp           : 1;
            uint32_t arp            : 1; //17
            uint32_t mul_cst        : 1;
            uint32_t brd_cst        : 1;
            uint32_t ip_ver_err     : 1; // not ip4 nor ip6
            uint32_t opt            : 1;
            uint32_t frag           : 1;
            uint32_t l4_err         : 4;
            uint32_t res2           : 1; // 27
            uint32_t ipv            : 1; // 0: ipv4 1:ipv6
            uint32_t ipse           : 1; // ipsec
            uint32_t ip_tcp_udp     : 2; // b00: not ip pkg; b01: ip b10:tcp ; b11 udp;
        }bits;
    } word4;

    union {
        uint32_t val;
        struct {
            uint32_t size       : 16;
            uint32_t res        : 5;
            uint32_t pool       : 4;
            uint32_t back       : 7;
        }bits;
    } word5;

    uint64_t next_ptr;
    uint32_t second_skip; // get value from regs
    uint32_t pad;
} str_rx_desc;

typedef union {
    uint32_t val;
    struct {
        uint32_t cf_rxbuf_first_skip_size2      : 4; // cacheline
        uint32_t cf_cache_line_l            : 3;
        uint32_t cf_rx_cfg_req_en           : 1; // reqest BMU receive buffer; 1:enable;0 disable
        uint32_t cf_cache_line_h            : 2;
        uint32_t cf_addr_mode               : 2; // address generat mode:
        uint32_t cf_rx_get_addr_mode        : 1;
        uint32_t cf_port_num                : 4;
        uint32_t cf_rx_align_num            : 2;
        uint32_t cf_pool_num                : 4;
        uint32_t cf_time_inf_en             : 1;
        uint32_t cf_rxbuf_no_first_skip_size    : 4;
        uint32_t cf_rxbuf_first_skip_size       : 4;
    } bits;
} str_rx_ctrl;

typedef union {
    uint32_t val;
    struct {
        uint32_t cf_instr_head_mode : 1;
        uint32_t res0               : 1;
        uint32_t cf_skip_len        : 7;
        uint32_t cf_parse_mode      : 2; // 01:from l2; 10:from ip; 11:reserve; 00: do not parse;
        uint32_t cf_march_offset    : 9;
        uint32_t res1               : 4;
        uint32_t cf_gen_id          : 8;
    } bits;
} str_rx_pkt_mode;

typedef union {
    uint32_t val;
    struct {
        uint32_t qos_mode       : 2; // 01: from vlan; 10: from ip; 11:from regs; 00: from regs;
        uint32_t port_qos       : 3; // qos_mode == 00;
        uint32_t arp_qos        : 3; // qos_mode == 11 && arp;
        uint32_t rarp_qos       : 3; // qos_mode == 11 && rarp;
        uint32_t mul_qos        : 3; // qos_mode == 11 && multicast;
        uint32_t brc_qos        : 3; // qos_mode == 11 && brocast;
        uint32_t icmp_qos       : 3; // qos_mode == 11 && icmp;
        uint32_t tcp_qos        : 3; // qos_mode == 11 && tcp;
        uint32_t udp_qos        : 3; // qos_mode == 11 && udp;
        uint32_t ip_qos         : 3; // qos_mode == 11 && ip;
        uint32_t oth_qos        : 3; // qos_mode == 11 && other types;
    } bits;
} str_qos_mode;

typedef union {
    uint32_t val;
    struct {
        uint32_t intrpt_time        : 6;
        uint32_t intrpt_pkt         : 6;
        uint32_t res                : 8;
        uint32_t sram_parity        : 1;
        uint32_t tx_ahb             : 1;
        uint32_t buf                : 1;
        uint32_t rel                : 1;
        uint32_t txcfg              : 1;
        uint32_t tx_drop            : 1;
        uint32_t rx_drop            : 1;
        uint32_t rx_frm             : 1;
        uint32_t rx_ahb             : 1;
        uint32_t mac_fifo           : 1;
        uint32_t rbreq              : 1;
        uint32_t we                 : 1;
    } bits;
} str_intrpt_def;

typedef struct {
    SysBusDevice parent_obj;

    NICState *nic;
    NICConf conf;
    qemu_irq irq;
    MemoryRegion mmio;

    uint32_t dev_idx;

    /* recv fifo */
    uint32_t rx_curidx;
    uint32_t rx_freeidx;
    uint32_t rx_freecnt;
    uint32_t rx_enable;

    /* tx fifo */
    uint32_t tx_cff_addr0;
    uint32_t tx_cff_addr1;
    uint32_t send_addr;
    uint32_t wb_addr;
    uint32_t tx_enable;
    uint32_t to_tx_cnt;

    uint32_t irq_en;
    uint32_t irq_stats;
    uint32_t irq_rawstats;

    /* cfg */
    uint32_t portmode;
    uint32_t duplex_mode;
    uint32_t transmit_ctl;

    /* debug */
    uint64_t total_send;
    uint64_t total_rcv;

    uint32_t regs[MAX_REG_OFFSET];

    /* recv fifo */
    uint32_t rx_buffer[RX_FIFO_DEPTH];
} hs1304_gmac_state;

static ssize_t hs1304_gmac_receive(NetClientState *nc, const uint8_t *buf, size_t size);

/* read */
static uint64_t hs1304_mac_readl(hs1304_gmac_state *s, hwaddr offset, unsigned size)
{
    uint64_t value = 0;

    switch (offset) {
        case PORT_EN_REG:
            /* 接收,发送使能 */
            value = s->rx_enable << 1 | s->tx_enable << 2;
            break;
        case TRANSMIT_CONTROL_REG:
            value = s->transmit_ctl;
            break;
        case STATION_ADDR_HIGH_2_REG:
            /* mac */
            value = s->conf.macaddr.a[4] | s->conf.macaddr.a[5] << 8;
            break;
        case STATION_ADDR_LOW_2_REG:
            /* mac */
            value = s->conf.macaddr.a[0] | s->conf.macaddr.a[1] << 8 | s->conf.macaddr.a[2] << 16 \
                    | s->conf.macaddr.a[3] << 24;
            break;
        case DUPLEX_TYPE_REG:
            value = s->duplex_mode;
            break;
        case PORT_MODE_REG:
            value = s->portmode;
            break;
        default:
            value = s->regs[offset >> REG_SHIFT];
            break;
    }

    return value;
}

static uint64_t hs1304_pcu_readl(hs1304_gmac_state *s, hwaddr offset, unsigned size)
{
    uint64_t value = 0;

    switch (offset) {
        case RX_CFF_ADDR_REG:
            if (s->rx_freecnt > 0) {
                s->rx_freecnt--;
                s->rx_freeidx = s->rx_freeidx == 0 ? (RX_FIFO_DEPTH - 1) : s->rx_freeidx - 1;
            }
            // else : there is no free buffer; do nothing;
            value = 0;
            break;
        case INTRPT_STAT_REG:
            /* 中断寄存器 */
            value = s->irq_stats;
            break;
        case INTRPT_MSK_REG:
            /* 中断使能寄存器 */
            value = s->irq_en;
            break;
        case CFF_DATA_NUM_REG:
            value = (((s->rx_freecnt) * 4) << 16) | (s->to_tx_cnt * 4);
            break;
        case FIFO_CURR_STATUS_REG:
            value = 0x33CC;
            break;
        default:
            value = s->regs[offset >> REG_SHIFT];
            break;
    }

    return value;
}

static uint64_t hs1304_gmac_readl(void *opaque, hwaddr offset, unsigned size)
{
    hs1304_gmac_state *s = HS1304_GMAC(opaque);
    uint64_t value = 0;

    if (offset >= MAX_OFFSET) {
        DEBUG_PRINT("[gmac_read][%d]: offset 0x%lx, is invalid\n", s->dev_idx, offset);
        return 0;
    }

    if (offset >= 0x400) {
        value = hs1304_pcu_readl(s, offset, size);
    } else {
        value = hs1304_mac_readl(s, offset, size);
    }

    DEBUG_PRINT("[gmac_read][%d]: offset 0x%lx, value 0x%lx\n", s->dev_idx, offset, value);
    return value;
}

static void hs1304_gmac_send_pkt(hs1304_gmac_state *s)
{
    /* parse tx cmd description */
    uint8_t send_buff[PACKET_DATA_SIZE];

    str_tx_word0 word0;
    str_tx_word1 word1;

    word0.val = s->tx_cff_addr0;
    word1.val = s->tx_cff_addr1;

    s->total_send++;
    /* Can't find how to parse description of the segment list mode . Only the data buffer mode is supported */
    if (word0.bits.sd != 0) {
        error_report("[gmac_tx][%d]: segment list mode is invalid\n", s->dev_idx);
        return;
    }
    address_space_read(&address_space_memory, s->send_addr, MEMTXATTRS_UNSPECIFIED, &send_buff, word1.bits.send_len);

    net_checksum_calculate(send_buff, word1.bits.send_len, CSUM_ALL);
    qemu_send_packet(qemu_get_queue(s->nic), &send_buff[0], word1.bits.send_len); // send
    if (word0.bits.cl == 1) {
        uint32_t tmp_data = 0;
        address_space_write(&address_space_memory, s->wb_addr, MEMTXATTRS_UNSPECIFIED, (uint8_t*)&tmp_data, 4);
    }

    // raise tx interrupt
    str_intrpt_def *irq_en = (str_intrpt_def*)&(s->irq_en);
    str_intrpt_def *irq_stats = (str_intrpt_def*)&(s->irq_stats);
    str_intrpt_def *irq_rawstats = (str_intrpt_def*)&(s->irq_rawstats);
    irq_rawstats->bits.txcfg = 1;
    if (irq_en->bits.txcfg == 1) {
        irq_stats->bits.txcfg = 1;
        qemu_irq_raise(s->irq);
    }
    return;
}

static void hs1304_mac_writel(hs1304_gmac_state *s, hwaddr offset, uint64_t val, unsigned size)
{
    switch (offset) {
        case PORT_EN_REG:
            s->rx_enable = (val & 0x2) >> 1;
            s->tx_enable = (val & 0x4) >> 2;
            break;
        case STATION_ADDR_HIGH_2_REG:
            /* 实际有6组mac寄存器，看驱动就用了这组 */
            s->conf.macaddr.a[4] = val & 0xff;
            s->conf.macaddr.a[5] = (val >> 8) & 0xff;
            qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
            break;
        case STATION_ADDR_LOW_2_REG:
            /* mac */
            s->conf.macaddr.a[0] = val & 0xff;
            s->conf.macaddr.a[1] = (val >> 8) & 0xff;
            s->conf.macaddr.a[2] = (val >> 16) & 0xff;
            s->conf.macaddr.a[3] = (val >> 24) & 0xff;
            qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
            break;
        case PORT_MODE_REG:
            s->portmode = (val == 0) ? MMAC_10M : MMAC_100M;
            break;
        case DUPLEX_TYPE_REG:
            s->duplex_mode = (val == 0) ? ETH_HALF_DUPLEX : ETH_FULL_DUPLEX;
            break;
        case TRANSMIT_CONTROL_REG:
            s->transmit_ctl = val;
            break;
        default:
            /* do nothing */
            DEBUG_PRINT("[gmac_write][%d]: do nothing\n", s->dev_idx);
            s->regs[offset >> REG_SHIFT] = val;
            break;
    }
    return;
}

static void hs1304_pcu_writel(hs1304_gmac_state *s, hwaddr offset, uint64_t val, unsigned size)
{
    if (offset >= TX_CFF_ADDR_0_REG && offset <= TX_CFF_ADDR_3_REG) {
        /* should write PCU_TX_CFF_ADDR_0_REG to PCU_TX_CFF_ADDR_3_REG continuously
            to complete the transmision of a packet */
        switch (offset) {
            case TX_CFF_ADDR_0_REG:
                s->tx_cff_addr0 = val;
                break;
            case TX_CFF_ADDR_1_REG:
                s->tx_cff_addr1 = val;
                break;
            case TX_CFF_ADDR_2_REG:
                s->send_addr = val;
                break;
            case TX_CFF_ADDR_3_REG:
                s->wb_addr = val;
                s->to_tx_cnt++;
                hs1304_gmac_send_pkt(s);
                s->to_tx_cnt--;
                break;
            default:
                break;
        }
        return;
    }

    switch (offset) {
        case RX_CFF_ADDR_REG:
            if (s->rx_freecnt < RX_FIFO_DEPTH) {
                s->rx_buffer[s->rx_freeidx] = (uint32_t)val;
                s->rx_freecnt++;
                s->rx_freeidx = (s->rx_freeidx + 1) % RX_FIFO_DEPTH;
            }
            // else, too more free buffers; do nothing;
            break;
        case INTRPT_MSK_REG:
            /* 中断使能 */
            s->irq_en = val;
            s->irq_stats = s->irq_en & s->irq_rawstats;
            if (s->irq_en && s->irq_stats) {
                qemu_irq_raise(s->irq);
            } else {
                qemu_irq_lower(s->irq);
            }
            break;
        case INTRPT_CLR_REG:
            /* clr 中断 */
            s->irq_rawstats &= ~val;
            s->irq_stats = s->irq_rawstats & s->irq_en;
            if (!s->irq_en || !s->irq_stats) {
                qemu_irq_lower(s->irq);
            }
            break;
        default:
            /* do nothing */
            s->regs[offset >> REG_SHIFT] = val;
            break;
    }
}

static void hs1304_gmac_writel(void *opaque, hwaddr offset, uint64_t val, unsigned size)
{
    hs1304_gmac_state *s = HS1304_GMAC(opaque);

    DEBUG_PRINT("[gmac_write][%d]: offset 0x%lx, value 0x%lx\n", s->dev_idx, offset, val);

    if (offset >= MAX_OFFSET) {
        error_report("[gmac_write][%d]: offset 0x%lx, is invalid\n", s->dev_idx, offset);
        return;
    }
    if (offset >= 0x400) {
        hs1304_pcu_writel(s, offset, val, size);
    } else {
        hs1304_mac_writel(s, offset, val, size);
    }

    return;
}

static bool hs1304_gmac_can_receive(NetClientState *nc)
{
    return true;
}
static uint32_t gmac_fill_tag(hs1304_gmac_state *s, const uint8_t *buf)
{
    return 0;
}

typedef struct {
    uint32_t pkg_type;
    uint32_t reg_offset;
    uint32_t offset;
} str_grp_type;

str_grp_type grp_type[] = {
    {.pkg_type = TYPE_ARP, .reg_offset = GRP_MODE0_REG, .offset = 22},
    {.pkg_type = TYPE_RARP, .reg_offset = GRP_MODE1_REG, .offset = 28},
    {.pkg_type = TYPE_MUL, .reg_offset = GRP_MODE1_REG, .offset = 24},
    {.pkg_type = TYPE_BRC, .reg_offset = GRP_MODE1_REG, .offset = 20},
    {.pkg_type = TYPE_ICMP, .reg_offset = GRP_MODE1_REG, .offset = 16},
    {.pkg_type = TYPE_TCP, .reg_offset = GRP_MODE1_REG, .offset = 12},
    {.pkg_type = TYPE_UDP, .reg_offset = GRP_MODE1_REG, .offset = 8},
    {.pkg_type = TYPE_IP, .reg_offset = GRP_MODE1_REG, .offset = 4},
    {.pkg_type = TYPE_OTH, .reg_offset = GRP_MODE1_REG, .offset = 0},
};

static uint32_t gmac_fill_grp(hs1304_gmac_state *s, uint32_t tag, uint32_t pkg_type, const uint8_t *buf)
{
    uint32_t val = 0;
    uint32_t grp_mode0_reg = s->regs[GRP_MODE0_REG >> 2];
    uint32_t grp_mode = grp_mode0_reg & 0x7;
    if (grp_mode == 0) {
        return (grp_mode0_reg >> 26) & 0xf;
    }

    if (grp_mode == 1) {
        return tag & 0xf;
    }

    if (grp_mode == 0x2) {
        for (uint32_t i = 0; i < TYPE_MAX; i++) {
            if (grp_type[i].pkg_type == pkg_type) {
                val = (s->regs[grp_type[i].reg_offset >> 2] >> grp_type[i].offset) & 0xf;
                return val;
            }
        }
        error_report("[%d]:grp_mode %d, type %d, can't get grp setting\n", s->dev_idx, grp_mode, pkg_type);
        return 0;
    }

    error_report("[%d]:grp_mode %d is not supported\n", s->dev_idx, grp_mode);
    return 0;
}

static uint32_t gmac_fill_qos(hs1304_gmac_state *s, str_qos_mode qos_mode, uint32_t pkg_type, const uint8_t *buf)
{
    uint32_t val = 0;

    switch (qos_mode.bits.qos_mode) {
        case 0x0:
            val = qos_mode.bits.port_qos;
            break;
        default:
            DEBUG_PRINT("[gmac_fill_qos][%d]:qos_mode %d is not supported!\n", s->dev_idx, qos_mode.bits.qos_mode);
            val = 0;
            break;
    }

    return val;
}

static uint32_t get_pkg_type(hs1304_gmac_state *s, const uint8_t *buf, size_t size)
{
    uint16_t tmp_type = ntohs(*((uint16_t*)&buf[12]));
    uint8_t *ip_head = NULL;
    str_rx_pkt_mode rx_pkt_mode;

    DEBUG_PRINT("[gmac_pkg_type][%d]: type 0x%x\n", s->dev_idx, tmp_type);

    if (tmp_type == 0x8100) {
        return TYPE_VLAN;
    }

    if (tmp_type < 0x5ff) { // 802.3
        tmp_type = ntohs(*((uint16_t*) &buf[22]));
        ip_head = (uint8_t*)&buf[24];
        DEBUG_PRINT("[gmac_pkg_type][%d]:802.3 type 0x%x\n", s->dev_idx, tmp_type);
    }

    switch (tmp_type) {
        case 0x0806:
            return TYPE_ARP;
        case 0x0835:
            return TYPE_RARP;
        case 0x8847:
            return TYPE_BRC;
        case 0x8848:
            return TYPE_MUL;
        case 0x8000:
            /* if set parse ip */
            rx_pkt_mode.val = s->regs[RX_PKT_MODE_REG >> 2];
            if (rx_pkt_mode.bits.cf_parse_mode == 0x2) {
                if (ip_head == NULL) {
                    ip_head = (uint8_t*)&buf[14];
                }

                uint32_t protocal = ip_head[10];
                if (protocal == 17) {
                    return TYPE_UDP;
                }
                if (protocal == 6) {
                    return TYPE_TCP;
                }
                if (protocal == 1) {
                    return TYPE_ICMP;
                }
                if (protocal == 2) {
                    return TYPE_IGMP;
                }
            }
            /* else */
            return TYPE_IP;
        default:
            break;
    }

    return TYPE_OTH;
}

static ssize_t hs1304_gmac_fill_desc(hs1304_gmac_state *s, hwaddr buff_addr, const uint8_t *buf, size_t size)
{
    str_rx_desc rx_desc = {0};
    str_rx_ctrl rx_ctrl;
    str_qos_mode qos_mode;
    str_rx_pkt_mode rx_pkt_mode;
    uint32_t pkg_type = get_pkg_type(s, buf, size);
    DEBUG_PRINT("[gmac_fill_desc][%d]: get pkg type 0x%x\n", s->dev_idx, pkg_type);

    qos_mode.val = s->regs[QOS_MODE_REG >> 2];
    rx_pkt_mode.val = s->regs[RX_PKT_MODE_REG >> 2];
    rx_ctrl.val = s->regs[RX_CTRL_REG >> 2];

    rx_desc.first_skip = rx_ctrl.bits.cf_rxbuf_first_skip_size * 32; // cache line : 32byte
    rx_desc.second_skip = rx_ctrl.bits.cf_rxbuf_first_skip_size2 * 32;
    rx_desc.pad = rx_ctrl.bits.cf_rx_align_num; // 00 no pad,

    rx_desc.word0.bits.tt = 0;
    rx_desc.word0.bits.Qos = gmac_fill_qos(s, qos_mode, pkg_type, buf);
    rx_desc.word0.bits.Gen_id = rx_pkt_mode.bits.cf_gen_id;
    rx_desc.word1.tag = gmac_fill_tag(s, buf);
    rx_desc.word0.bits.grp = gmac_fill_grp(s, rx_desc.word1.tag, pkg_type, buf);

    rx_desc.word2.bits.all_skip = (rx_desc.first_skip + rx_desc.second_skip) / 32;
    rx_desc.word2.bits.port_num = rx_ctrl.bits.cf_port_num;
    rx_desc.word2.bits.len = size; // fixme::should + descriptor ????or not???

    rx_desc.word3.bits.vlan = (pkg_type == TYPE_VLAN) ? (uint16_t)*((uint16_t*)&buf[14]) : 0;
    rx_desc.word3.bits.lp_offset = 14; // fixme:: there are  6 types; 14, 22??
    rx_desc.word3.bits.buf_num = 1;

    rx_desc.word4.bits.ptp_err = 0;
    rx_desc.word4.bits.ptp_flag = 0;
    rx_desc.word4.bits.pm = 0; // do not parse pkg
    rx_desc.word4.bits.key_match = 1; // match
    rx_desc.word4.bits.l2_err = 0;
    rx_desc.word4.bits.l3_err = 0;
    rx_desc.word4.bits.drop = 0;
    rx_desc.word4.bits.vlan_flag = (pkg_type == TYPE_VLAN) ? 1:0;
    rx_desc.word4.bits.icmp = (pkg_type == TYPE_ICMP) ? 1:0;
    rx_desc.word4.bits.rarp = (pkg_type == TYPE_RARP) ? 1:0;
    rx_desc.word4.bits.arp = (pkg_type == TYPE_ARP) ? 1:0;
    rx_desc.word4.bits.mul_cst = (pkg_type == TYPE_MUL) ? 1:0;
    rx_desc.word4.bits.brd_cst = (pkg_type == TYPE_BRC) ? 1:0;
    rx_desc.word4.bits.ip_ver_err = 0;
    rx_desc.word4.bits.opt = 0;
    rx_desc.word4.bits.frag = 0;
    rx_desc.word4.bits.l4_err = 0;
    rx_desc.word4.bits.ipv = 0; // fixme::get_ipversion(s, buf, size); //0:ipv4, 1:ipv6
    rx_desc.word4.bits.ipse = 0;
    rx_desc.word4.bits.ip_tcp_udp = (pkg_type==TYPE_IP) ? 1 : ((pkg_type==TYPE_TCP) ? 2 : \
                                    ((pkg_type==TYPE_UDP) ? 3 : 0));
    rx_desc.word5.bits.size = size;
    rx_desc.word5.bits.pool = 0;
    rx_desc.word5.bits.back = 0;
    rx_desc.next_ptr = 0x0; // fixme::

    // big endian
    uint32_t *temp_data = (uint32_t*)&rx_desc.word0;
    for (uint32_t i = 0; i < 8; i++, temp_data++) {
        *temp_data = htonl(*temp_data);
    }

    address_space_write(&address_space_memory, buff_addr + rx_desc.first_skip,
                        MEMTXATTRS_UNSPECIFIED, &rx_desc.word0, 32);

    return rx_desc.first_skip + 32 + rx_desc.second_skip;
}

static ssize_t hs1304_gmac_receive(NetClientState *nc, const uint8_t *buf, size_t size)
{
    hs1304_gmac_state *s = qemu_get_nic_opaque(nc);
    hwaddr buff_addr;
    uint32_t len;
    str_rx_ctrl rx_ctrl;
    rx_ctrl.val = s->regs[RX_CTRL_REG >> 2];

    if (s->rx_enable == 0) {
        return -1;
    }

    /* get free fifo */
    if (s->rx_freecnt <= 0 || s->rx_freecnt > RX_FIFO_DEPTH) {
        DEBUG_PRINT("[gmac][%d]: recv fifo is full\n", s->dev_idx);
        return -1;
    }

    if (size > PACKET_DATA_SIZE) {
        DEBUG_PRINT("[gmac][%d]: recv msg len 0x%x is invalid\n", s->dev_idx, (uint32_t)size)
        return -1;
    }

    buff_addr = s->rx_buffer[s->rx_curidx];

    /* fill descriptor */
    len = hs1304_gmac_fill_desc(s, buff_addr, buf, size);
    if (len == 0) {
        DEBUG_PRINT("[gmac][%d]: fill desc failed\n", s->dev_idx);
        return -1;
    }

    // check pad
    len += rx_ctrl.bits.cf_rx_align_num;
    address_space_write(&address_space_memory, buff_addr + len,
                        MEMTXATTRS_UNSPECIFIED, buf, size);

    /* raise interrupt */
    s->rx_curidx = (s->rx_curidx + 1) % RX_FIFO_DEPTH;
    s->rx_freecnt--;
    s->total_rcv++;

    str_intrpt_def *irq_en = (str_intrpt_def*)&(s->irq_en);
    str_intrpt_def *irq_stats = (str_intrpt_def*)&(s->irq_stats);
    str_intrpt_def *irq_rawstats = (str_intrpt_def*)&(s->irq_rawstats);
    irq_rawstats->bits.rx_frm = 1;
    if (irq_en->bits.rx_frm == 1) {
        irq_stats->bits.rx_frm = 1;
        qemu_irq_raise(s->irq);
    }

    return size;
}

static void hs1304_gmac_set_link(NetClientState *nc)
{
}

/* ops */
static const MemoryRegionOps hs1304_gmac_mem_ops = {
    .read = hs1304_gmac_readl,
    .write = hs1304_gmac_writel,
    .valid.min_access_size = 4,
    .valid.max_access_size = 4,
    .endianness = DEVICE_LITTLE_ENDIAN,
};

static NetClientInfo net_hs1304_gmac_info = {
    .type = NET_CLIENT_DRIVER_NIC,
    .size = sizeof(NICState),
    .can_receive = hs1304_gmac_can_receive,
    .receive = hs1304_gmac_receive,
    .link_status_changed = hs1304_gmac_set_link,
};

/* reset */
static void hs1304_gmac_reset(DeviceState *d)
{
    hs1304_gmac_state *s = HS1304_GMAC(d);
    uint32_t offset = offsetof(hs1304_gmac_state, rx_curidx);

    memset((uint8_t*)s+offset, 0, sizeof(hs1304_gmac_state) - offset);
}

/* realize */
static void hs1304_gmac_realize(DeviceState *dev, Error **errp)
{
    hs1304_gmac_state *s = HS1304_GMAC(OBJECT(dev));

    qemu_macaddr_default_if_unset(&s->conf.macaddr);

    memory_region_init_io(&s->mmio, OBJECT(dev), &hs1304_gmac_mem_ops, s,
                          TYPE_HS1304_GMAC, MAX_OFFSET);
    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio);

    sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);

    s->nic = qemu_new_nic(&net_hs1304_gmac_info, &s->conf,
                          object_get_typename(OBJECT(dev)), dev->id, s);
    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
}

static Property hs1304_gmac_properties[] = {
    DEFINE_NIC_PROPERTIES(hs1304_gmac_state, conf), /* mac address 和 netdev配置 */
    DEFINE_PROP_UINT32("index", hs1304_gmac_state, dev_idx, 0x0),
    DEFINE_PROP_END_OF_LIST(), /* 其他配置还没有想好 */
};

static const VMStateDescription hs1304_gmac_vmstate = {
    .name = TYPE_HS1304_GMAC,
    .version_id = 1,
    .minimum_version_id = 1,
    .fields = (VMStateField[]) {
        VMSTATE_MACADDR(conf.macaddr, hs1304_gmac_state),
        VMSTATE_UINT32(dev_idx, hs1304_gmac_state),
        VMSTATE_UINT32(rx_curidx, hs1304_gmac_state),
        VMSTATE_UINT32(rx_freeidx, hs1304_gmac_state),
        VMSTATE_UINT32(rx_freecnt, hs1304_gmac_state),
        VMSTATE_UINT32(rx_enable, hs1304_gmac_state),
        VMSTATE_UINT32(tx_cff_addr0, hs1304_gmac_state),
        VMSTATE_UINT32(tx_cff_addr1, hs1304_gmac_state),
        VMSTATE_UINT32(send_addr, hs1304_gmac_state),
        VMSTATE_UINT32(wb_addr, hs1304_gmac_state),
        VMSTATE_UINT32(tx_enable, hs1304_gmac_state),
        VMSTATE_UINT32(to_tx_cnt, hs1304_gmac_state),
        VMSTATE_UINT32(irq_en, hs1304_gmac_state),
        VMSTATE_UINT32(irq_stats, hs1304_gmac_state),
        VMSTATE_UINT32(irq_rawstats, hs1304_gmac_state),
        VMSTATE_UINT32(portmode, hs1304_gmac_state),
        VMSTATE_UINT32(duplex_mode, hs1304_gmac_state),
        VMSTATE_UINT32(transmit_ctl, hs1304_gmac_state),
        VMSTATE_UINT64(total_send, hs1304_gmac_state),
        VMSTATE_UINT64(total_rcv, hs1304_gmac_state),
        VMSTATE_UINT32_ARRAY(regs, hs1304_gmac_state, MAX_REG_OFFSET),
        VMSTATE_UINT32_ARRAY(rx_buffer, hs1304_gmac_state, RX_FIFO_DEPTH),
        VMSTATE_END_OF_LIST()
    }
};

/* class init */
static void hs1304_gmac_class_init(ObjectClass *klass, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(klass);

    dc->reset = hs1304_gmac_reset;
    device_class_set_props(dc, hs1304_gmac_properties);
    dc->vmsd = &hs1304_gmac_vmstate;
    dc->realize = hs1304_gmac_realize;
}

/* register */
static const TypeInfo hs1304_gmac_type_info = {
    .name          = TYPE_HS1304_GMAC,
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(hs1304_gmac_state),
    .class_init    = hs1304_gmac_class_init,
};

static void hs1304_gmac_register_types(void)
{
    type_register_static(&hs1304_gmac_type_info);
}

type_init(hs1304_gmac_register_types)
